using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;

namespace SchemaProvider
{
    public abstract class TableCodeProviderBase : ITableCodeProvider
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="foreignKeys"></param>
        /// <param name="columnName"></param>
        /// <returns></returns>
        private ForeignKey GetForeignKey(List<ForeignKey> foreignKeys,string columnName)
        {
            foreach (ForeignKey foreignKey in foreignKeys)
            {
                if (foreignKey.Key == columnName)
                {
                    return foreignKey;
                }
            }
            return new ForeignKey();
        }


        /// <summary>
        /// Converts a column name to a field name. The derived class must implement it.
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public string ToFieldName(string columnName)
        {
            return "_" + CodeUtility.PascalCaseToCamelCase(ToPropertyName(columnName));
        }

        /// <summary>
        /// Converts a column name to a property name. The derived class must implement it.
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public abstract string ToPropertyName(string columnName);




        #region ITableCodeProvider Members

        public string MapMemberVariablesBlock(DataColumnCollection columns)
        {
            StringBuilder content = new StringBuilder();
            if (columns.Count <= 0)
            {
                return content.ToString();
            }
            content.Append("\n");
            foreach (DataColumn column in columns)
            {
                content.Append(CodeUtility.CreateTabSpaces(2) + "private " + CodeUtility.GetDataType(column) + " " + this.ToFieldName(column.ColumnName) + ";\n");
            }
            return content.ToString();
        }

        public string MapPropertiesBlock(DataColumnCollection columns, bool needGateway, string identityColumnName, string primaryKeyColumnName, List<ForeignKey> foreignKeys)
        {
            StringBuilder content = new StringBuilder();
            if (columns.Count <= 0)
            {
                return content.ToString();
            }

            content.Append("\n");
            foreach (DataColumn column in columns)
            {
                //attribute statement
                if (needGateway)
                {
                    AppendPropertyAttributes(identityColumnName, primaryKeyColumnName, foreignKeys, content, column);
                }

                //property block
                AppendProperty(needGateway, content, column);
            }

            return content.ToString();
        }

        private void AppendPropertyAttributes(string identityColumnName, string primaryKeyColumnName, List<ForeignKey> foreignKeys, StringBuilder content, DataColumn column)
        {
            content.Append(CodeUtility.CreateTabSpaces(2));
            if (IsIdentityColumn(identityColumnName, column) && IsPrimaryKeyColumn(primaryKeyColumnName, column))
            {
                content.Append("[Column(ColumnName = \"" + column.ColumnName + "\",IsIdentity = true,IsPrimaryKey = true)]\n");
            }
            else if (IsIdentityColumn(identityColumnName, column))
            {
                content.Append("[Column(ColumnName = \"" + column.ColumnName + "\",IsIdentity = true)]\n");
            }
            else if (IsPrimaryKeyColumn(primaryKeyColumnName, column))
            {
                content.Append("[Column(ColumnName = \"" + column.ColumnName + "\",IsPrimaryKey = true)]\n");
            }
            else if (foreignKeys.Exists(delegate(ForeignKey foreignKey) { return IsForeignKey(column, ref foreignKey); }))
            {
                content.Append("[Column(ColumnName = \"" + column.ColumnName + "\",IsForeignKey = true,ReferenceType=typeof(" +
                    MapClassName(GetForeignKey(foreignKeys, column.ColumnName).ReferenceTableName) + "))]\n");
            }
            else
            {
                content.Append("[Column(ColumnName = \"" + column.ColumnName + "\")]\n");
            }
        }

        private static bool IsForeignKey(DataColumn column, ref ForeignKey foreignKey)
        {
            if (string.IsNullOrEmpty(foreignKey.Key))
            {
                return false;
            }

            return foreignKey.Key.ToUpper() == column.ColumnName.ToUpper();
        }

        private static bool IsPrimaryKeyColumn(string primaryKeyColumnName, DataColumn column)
        {
            if (string.IsNullOrEmpty(primaryKeyColumnName))
            {
                return false;
            }

            return column.ColumnName.ToUpper() == primaryKeyColumnName.ToUpper();
        }

        private static bool IsIdentityColumn(string identityColumnName, DataColumn column)
        {
            if (string.IsNullOrEmpty(identityColumnName))
            {
                return false;
            }

            return column.ColumnName.ToUpper() == identityColumnName.ToUpper();
        }

        private void AppendProperty(bool needGateway, StringBuilder content, DataColumn column)
        {
            string propertyName = this.ToPropertyName(column.ColumnName);
            string fieldName = this.ToFieldName(column.ColumnName);

            content.Append(CodeUtility.CreateTabSpaces(2) + "public " + CodeUtility.GetDataType(column) + " " + propertyName + "\n");
            content.Append(CodeUtility.CreateTabSpaces(2) + "{\n");
            content.Append(CodeUtility.CreateTabSpaces(3) + "get\n");
            content.Append(CodeUtility.CreateTabSpaces(3) + "{\n");
            content.Append(CodeUtility.CreateTabSpaces(4) + "return " + fieldName + ";\n");
            content.Append(CodeUtility.CreateTabSpaces(3) + "}\n");
            content.Append(CodeUtility.CreateTabSpaces(3) + "set\n");
            content.Append(CodeUtility.CreateTabSpaces(3) + "{\n");
            content.Append(CodeUtility.CreateTabSpaces(4) + fieldName + "=value;\n");
            if (needGateway)
            {
                content.Append(CodeUtility.CreateTabSpaces(4) + "base.OnPropertyChanged(MethodInfo.GetCurrentMethod());\n");
            }
            content.Append(CodeUtility.CreateTabSpaces(3) + "}\n");
            content.Append(CodeUtility.CreateTabSpaces(2) + "}\n\r");
        }

        #endregion

        #region ICodeProvider Members

        public virtual string MapClassAttribute(string name)
        {
            return "[Table(TableName = \"" + name + "\")]";
        }

        public abstract string MapClassName(string name);


        public virtual string MapBaseClassName(string name)
        {
            string baseClassName = "TableGateway";
            return baseClassName + "<" + this.MapClassName(name) + ">";
        }

        #endregion
    }
}
